package com.ybear.ybnetworkutil.request;

import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.json.JSONObject;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.TreeMap;

/**
 * 请求的参数
 * @see #toMap()
 * @see #toParamString()
 * @see #toParamJson()
 *
 * @see #addParam(String, Object)
 * @see #addParam(String, String[])
 * @see #addParamAll(Map)
 *
 * @see #getMap()
 * @see #clear()
 */
public class Param {
    private static final String KEY_PARAM_RAW = "KEY_PARAM_FOR_RAW";
    @NonNull
    private final TreeMap<String, Object> mParams = new TreeMap<>();

    public Param() {}

    public Param(@Nullable Map<String, Object> param) {
        addParamAll( param );
    }

    @NonNull
    @Override
    public String toString() {
        return "Param{" +
                "mParams=" + mParams +
                '}';
    }

    /**
     * 反射继承类所有参数为Map集合
     * @return  {@link Map<String>}
     */
    public TreeMap<String, Object> toMap() {
        TreeMap<String, Object> map = new TreeMap<>();
        Field[] fields = getClass().getDeclaredFields();
        try {
            for (Field field : fields) {
                if( field == null ) continue;
                field.setAccessible(true);
                map.put( field.getName(), field.get( this ) );
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    @NonNull
    public String toParamString() {
        StringBuilder sb = new StringBuilder();
        if( mParams.size() == 0 ) {
            return "";
        } else if ( mParams.containsKey( KEY_PARAM_RAW ) ) {
            Object raw = mParams.get( KEY_PARAM_RAW );
            return raw != null ? raw.toString() : "";
        } else {
            for( String key : mParams.keySet() ) {
                Object val = ( val = mParams.get(key) ) == null ? "" : val.toString();
                sb.append( key ).append( "=" ).append( val ).append( "&" );
            }
            String s = sb.toString();
            return s.endsWith( "&" ) ? s.substring( 0, s.length() - 1 ) : s;
        }
    }

    @NonNull
    public String toParamJson() {
        if( mParams.containsKey( KEY_PARAM_RAW ) ) {
            String param = String.valueOf( mParams.get( KEY_PARAM_RAW ) );
            if( !TextUtils.isEmpty( param ) && param.contains( "&" ) ) {
                String[] paramKV = param.split( "&" );
                for( String pkv : paramKV ) {
                    if( TextUtils.isEmpty( pkv ) || !pkv.contains( "=" ) ) continue;
                    String[] skv = pkv.split( "=" );
                    mParams.put( skv[ 0 ], skv[ 1 ] );
                }
            }
            mParams.remove( KEY_PARAM_RAW );
        }
        return new JSONObject( mParams ).toString();
    }

    @NonNull
    public String toParamBodyString() {
        if( mParams.containsKey( KEY_PARAM_RAW ) ) {
            String param = String.valueOf( mParams.get( KEY_PARAM_RAW ) );
            if( !TextUtils.isEmpty( param ) && !param.contains( "&" ) ) return param;
        }
        return toParamJson();
    }

    @NonNull
    public String addParam(@NonNull String key, @Nullable Object val) {
        Object ret = mParams.put( key, val != null ? val.toString() : "" );
        return ret != null ? ret.toString() : "";
    }

    @NonNull
    public String addParam(@NonNull String key, @Nullable String[] val) {
        if( val == null || val.length == 0 ) return addParam( key, new String[0] );
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < val.length; i++) {
            sb.append("\"").append( val[ i ] ).append("\"");
            if( i < val.length - 1) sb.append(",");
        }
        sb.append("]");
        return addParam( key, sb );
    }

    @Nullable
    public Object setParam(@Nullable String param) {
        this.mParams.clear();
        return this.mParams.put( KEY_PARAM_RAW, param );
    }

    @NonNull
    public Param addParamAll(@Nullable Map<String, Object> paramAll) {
        if( paramAll == null ) return this;
        mParams.putAll( paramAll );
        return this;
    }

    @NonNull
    public TreeMap<String, Object> getMap() { return this.mParams; }

    public void clear() {
        this.mParams.clear();
    }
}